package stone926.mods.more_enchantments.mixins;

import net.minecraft.entity.EntityType;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.mob.MagmaCubeEntity;
import net.minecraft.entity.mob.MobEntity;
import net.minecraft.entity.mob.Monster;
import net.minecraft.entity.mob.SlimeEntity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.text.Text;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import stone926.mods.more_enchantments.interfaces.IEntitySpawnedByFlower;
import stone926.mods.more_enchantments.interfaces.ISlimeEntity;
import stone926.mods.more_enchantments.util.EntityUtil;

import static stone926.mods.more_enchantments.nbt.CustomNbtKeys.MAGMA_CUBE.*;

@Mixin(MagmaCubeEntity.class)
public class MagmaCubeEntityMixin extends SlimeEntity {

  public MagmaCubeEntityMixin(EntityType<? extends SlimeEntity> entityType, World world) {
    super(entityType, world);
  }

  @Redirect(method = "jump", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/mob/MagmaCubeEntity;setVelocity(DDD)V"))
  private void lowJump(MagmaCubeEntity magma, double x, double y, double z) {
    if (((ISlimeEntity) magma).shouldLowJump()) {
      magma.setVelocity(x, this.getJumpVelocity(), z);
    } else magma.setVelocity(x, y, z);
  }

  @Mixin(SlimeEntity.class)
  private static abstract class SlimeEntityMixin extends MobEntity implements Monster, ISlimeEntity {

    @Shadow
    public abstract int getSize();

    @Unique private boolean lowJump = false;
    @Unique private int preventSplitSize = -1;
    @Unique private int attackSpeed = -1;

    protected SlimeEntityMixin(EntityType<? extends MobEntity> entityType, World world) {
      super(entityType, world);
    }

    @SuppressWarnings("all")
    @Inject(method = "readCustomDataFromNbt", at = @At("TAIL"))
    private void readCustomDataFromNbt(NbtCompound nbt, CallbackInfo ci) {
      if ((SlimeEntity) (Object) this instanceof MagmaCubeEntity) {
        if (nbt.contains(LOW_JUMP)) setLowJump(nbt.getBoolean(LOW_JUMP));
        if (nbt.contains(PREVENT_SPLIT_SIZE)) setPreventSplitSize(nbt.getInt(PREVENT_SPLIT_SIZE));
        if (nbt.contains(ATTACK_SPEED)) setAttackSpeed(nbt.getInt(ATTACK_SPEED));
      }
    }

    @SuppressWarnings("all")
    @Inject(method = "writeCustomDataToNbt", at = @At("TAIL"))
    private void writeCustomDataToNbt(NbtCompound nbt, CallbackInfo ci) {
      if ((SlimeEntity) (Object) this instanceof MagmaCubeEntity) {
        nbt.putBoolean(LOW_JUMP, shouldLowJump());
        nbt.putInt(PREVENT_SPLIT_SIZE, getPreventSplitSize());
        nbt.putInt(ATTACK_SPEED, getAttackSpeed());
      }
    }

    @Override
    public boolean shouldLowJump() {
      return lowJump;
    }

    @Override
    public void setLowJump(boolean lowJump) {
      this.lowJump = lowJump;
    }

    @Override
    public int getPreventSplitSize() {
      return preventSplitSize;
    }

    @Override
    public void setPreventSplitSize(int size) {
      this.preventSplitSize = size;
    }

    @Unique
    @Override
    public int getAttackSpeed() {
      return attackSpeed;
    }

    @Unique
    @Override
    public void setAttackSpeed(int attackSpeed) {
      this.attackSpeed = attackSpeed;
    }

    @Inject(method = "remove", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/mob/SlimeEntity;getCustomName()Lnet/minecraft/text/Text;"), cancellable = true)
    private void preventSplit(RemovalReason reason, CallbackInfo ci) {
      if (RemovalReason.KILLED.equals(reason) && ((IEntitySpawnedByFlower) this).isSpawnedByMandala() && getSize() <= getPreventSplitSize()) {
        ci.cancel();
        super.remove(reason);
      }
    }

    @SuppressWarnings("all")
    @Inject(method = "remove", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;spawnEntity(Lnet/minecraft/entity/Entity;)Z"), locals = LocalCapture.CAPTURE_FAILSOFT)
    private void setSpawnedByMandala(
      RemovalReason reason, CallbackInfo ci,
      int i, Text text, boolean bl, float f, int j, int k, int l, float g, float h, SlimeEntity slime
    ) {
      ((IEntitySpawnedByFlower) slime).setSpawnedByMandala(((IEntitySpawnedByFlower) this).isSpawnedByMandala());
      ((ISlimeEntity) slime).setLowJump(shouldLowJump());
      ((ISlimeEntity) slime).setPreventSplitSize(getPreventSplitSize());
      ((ISlimeEntity) slime).setAttackSpeed(getAttackSpeed());
      if (((IEntitySpawnedByFlower) slime).isSpawnedByMandala()) {
        slime.getAttributes().setFrom(((SlimeEntity) (Object) this).getAttributes());
        for (EquipmentSlot slot : EquipmentSlot.values()) {
          EntityUtil.equipStackAndSetDropChance(slime, slot, getEquippedStack(slot), 0.03F);
        }
      }
    }

    @Inject(method = "damage", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/mob/SlimeEntity;applyDamageEffects(Lnet/minecraft/entity/LivingEntity;Lnet/minecraft/entity/Entity;)V"))
    private void fastAttack(LivingEntity target, CallbackInfo ci) {
      if (getAttackSpeed() >= 0) {
        target.timeUntilRegen = getAttackSpeed();
      }
    }

  }

  @Mixin(LivingEntity.class)
  private static class LivingEntityMixin {

    @SuppressWarnings("all")
    @Inject(method = "isInsideWall", at = @At("HEAD"), cancellable = true)
    private void preventSuffocate(CallbackInfoReturnable<Boolean> cir) {
      if ((LivingEntity) (Object) this instanceof MagmaCubeEntity && ((IEntitySpawnedByFlower) this).isSpawnedByMandala()) {
        cir.setReturnValue(false);
      }
    }

  }

}
